/*
 * dim-sum操作系统 -- 处理器初始化
 *
 * Copyright (C) 2022 国科础石(重庆)软件有限公司
 *
 * 作者: Dong Peng <w-pengdong@kernelsoft.com>
 *
 * License terms: GNU General Public License (GPL) version 3
 *
 */

#include <linux/compiler.h>
#include <dim-sum/devtree.h>
#include <dim-sum/types.h>
#include <dim-sum/init.h>
#include <dim-sum/libfdt.h>
#include <dim-sum/stringify.h>
#include <asm/barrier.h>
#include <asm/devtree.h>
#include <asm/hyp.h>
#include <asm/sbi.h>
#include <asm/head64.h>
#include <asm/sections.h>
#include <asm/processor.h>

extern pgd_t pgtbl_root[];
pgd_t trampoline_pg_dir[PTRS_PER_PGD] __initdata __aligned(PAGE_SIZE);

#define NUM_SWAPPER_PMDS ((uintptr_t)-PAGE_OFFSET >> PGDIR_SHIFT)
pmd_t pgtbl_pmd[PTRS_PER_PMD*((-PAGE_OFFSET)/PGDIR_SIZE)] __page_aligned_bss;
pmd_t trampoline_pmd[PTRS_PER_PGD] __initdata __aligned(PAGE_SIZE);

struct accurate_counter hart_lottery __section(".sdata") = { 0 };
unsigned long boot_cpu_hartid;
unsigned long va_pa_offset;
unsigned long pfn_base;

void __init riscv_die(void)
{
	while (1) {
		cpu_do_idle();
	}
}

asmlinkage void __init create_start_mmutbl(void)
{
	uintptr_t i;
	uintptr_t pa = (uintptr_t) kernel_text_start;
	pgprot_t prot = __pgprot(pgprot_val(PAGE_KERNEL) | _PAGE_EXEC);

	va_pa_offset = PAGE_OFFSET - pa;
	phys_addr_origin = pa;
	
	pfn_base = PFN_DOWN(pa);

	/* Sanity check alignment and size */
	BUG_ON((PAGE_OFFSET % PGDIR_SIZE) != 0);
	BUG_ON((pa % (PAGE_SIZE * PTRS_PER_PTE)) != 0);

	trampoline_pg_dir[(PAGE_OFFSET >> PGDIR_SHIFT) % PTRS_PER_PGD] =
		head_pfn_pgd(PFN_DOWN((uintptr_t)trampoline_pmd),
			__pgprot(_PAGE_TABLE));
	trampoline_pmd[0] = head_pfn_pmd(PFN_DOWN(pa), prot);

	/**
	 * 这里将内核态虚拟地址映射到起始物理地址上
	 * 映射大小为 0xffffffffffffffff - 0xffffffe000000000 + 0x1 = 0x2000000000
	 * 对于 qemu 来说是将 	0xffffffe000000000 	映射到 	0x0000000080000000
	 * 对于 JH7110 来说是将 0xffffffe000000000 	映射到 	0x0000000040200000
	 */
	for (i = 0; i < (-PAGE_OFFSET)/PGDIR_SIZE; ++i) {
		size_t o = (PAGE_OFFSET >> PGDIR_SHIFT) % PTRS_PER_PGD + i;
		pgtbl_root[o] =
			head_pfn_pgd(PFN_DOWN((uintptr_t)pgtbl_pmd) + i,
				__pgprot(_PAGE_TABLE));
	}
	for (i = 0; i < ARRAY_SIZE(pgtbl_pmd); i++)
		pgtbl_pmd[i] = head_pfn_pmd(PFN_DOWN(pa + i * PMD_SIZE), prot);
}

static inline u32 _rev32(u32 val)
{
	u32 ret = 0;
	ret |= ((val >> 24) & 0xff); 
	ret |= ((val >> 8) & 0xff00);
	ret |= ((val << 8) & 0xff0000); 
	ret |= ((val << 24) & 0xff000000);
	return ret;
}

static inline unsigned long
	_fdt_size(virt_addr_t dtb_start)
{
	u32 *src = (u32 *)dtb_start;

	if (_rev32(src[0]) != FDT_MAGIC)
		riscv_die();

	return (unsigned long)(_rev32(src[1]));
}

void __init parse_dtb(unsigned int hartid, void *dtb)
{
	devtree_phys_base = (virt_addr_t)dtb & MMU_BLOCK_MASK;
	devtree_virt_base = ((virt_addr_t)dtb + va_pa_offset) & MMU_BLOCK_MASK;
	devtree_virt_size = _fdt_size((virt_addr_t)dtb + va_pa_offset);
	devtree_virt = devtree_virt_base + ((virt_addr_t)dtb & (MMU_BLOCK_SIZE - 1));
	boot_cpu_hartid = hartid;
}